/*
尽可能还原 Promise 中的每一个 API, 并通过注释的方式描述思路和原理.
*/

/** 
 * 自定义MyPromise
 * 通过链式调用的方式来实现异步调用,防止出现回调地狱的情况
 * MyPromise有三种状态，等待,成功和失败,初始状态为等待
 * 状态只能由等待转变为成功或失败,状态在转变为成功|失败之后不能在修改状态.
 * 主要功能: 
 * 1.状态的同步和异步变更,通过then方法来传递回调,根据状态变更执行相应的回调
 * 2.then的链式调用
 * 3.如果当前then函数中未传入函数则跳过,并且把当前then的上一个then的值传给下一个then
 * 4.MyPromise可以调用多个then函数 
 * 5.MyPromise在then函数中不能通过返回自己的方式循环调用自己
 * 6.then函数中的回调函数都为微任务
 * 
 * 说明:
 * 方法注释中的then函数用域指代MyPromise中用于链式调用的所有函数,不单单只是then函数
*/
const WAITONG = 'waiting' // 等待
const SOLVE = 'solve' // 成功
const FAULT = 'fault' // 失败

class MyPromise {
    constructor(fn) {
        try {
            fn(this.resolve, this.reject)
        } catch (err) {
            this.reject(new Error(err))
        }
    }
    // 状态
    _state = WAITONG
    // 状态转变为存储SOLVE状态时传入的值
    _value = undefined
    // 状态转变为FAULT时，存储错误信息
    _errMsg = undefined
    // 成功的回调
    _solveCb = []
    // 失败的回调
    _faultCb = []

    /**
     * 接收状态变更时的回调函数,并返回一个新MyPromise实例实现链式调用
     * 如果当前then函数中未传入函数则跳过,并且把当前then的上一个then的值传给下一个then
     * 如果fn的返回值是一个MyPromise实例时,下一个then函数需要等待返回的MyPromise执行后才能调用
     * 如果fn或errFn存在异常则捕获异常并传递给下一个异常处理函数
     * fn&errFn回调函数都为微任务
     * @param {function} fn 在resolve时，调用的回调
     * @param {function} errFn 在reject时，调用的回调
     */
    then(fn, errFn) {
        // MyPromise状态为SOLVE(成功)时执行的回调
        const thenSolveCb = (fn, resolve, reject) => {
            // 将回调异步执行
            queueMicrotask(() => {
                if (typeof fn === 'function') {
                    let value
    
                    // 如果在fn函数调用的过程中存在异常则向reject中传递异常信息
                    try{
                        value = fn(this._value)
                    } catch (err) {
                        return reject(new Error(err))
                    }
                    
                    // 发现循环调用自己的话则向reject传递异常信息
                    if (this === value) return reject(new Error('MyPromise不能循环调用自身'))
    
                    if (value instanceof MyPromise) {
                        // fn的返回值为一个MyPromise的实例时,则等待value解决之后在执行
                        value.then((res) => {
                            resolve(res)
                        })
                    } else {
                        resolve(value)
                    }
                } else {
                    // 如果fn不是一个函数,则直接传入当前myPromise实例存储的value值
                    resolve(this._value)
                }
            })
        }
        // MyPromise状态为FAULT(失败)时执行的回调
        const thenFaultCb = (errFn, reject) => {
            queueMicrotask(() => {
                let errMsg
            
                if (typeof errFn === 'function') {
                    try{
                        errMsg = errFn(this._errMsg)
                    } catch (err) {
                        return reject(new Error(err))
                    }

                    if (errMsg instanceof MyPromise) {
                        // errFn的返回值为一个MyPromise的实例时,则等待errMsg解决之后在执行
                        errMsg.then(undefined, (res) => {
                            reject(res)
                        })
                    } else {
                        reject(errMsg)
                    }
                } else { 
                    reject(this._errMsg)
                }
            })
        }

        // 返回一个新的MyPromise实例,以实现链式调用
        return new MyPromise((resolve, reject) => {
            if (this._state === SOLVE) {
                // 状态为成功时
                thenSolveCb(fn, resolve, reject)
            } else if (this._state === FAULT) {
                // 状态失败时
                thenFaultCb(errFn, reject)
            } else {
                // 状态为等待时
                // then在状态变更之前调用时，将回调函数缓存起来(状态异步变更)
                this._solveCb.push((value) => {
                    thenSolveCb(fn, resolve, reject)
                })
                this._faultCb.push((errMsg) => {
                    thenFaultCb(errFn, reject)
                })
            } 
        })
    }

    /**
     * MyPromise为失败时捕获错误信息
     * 支持链式调用
     * @param {function} failCb 失败回调
     * */ 
    catch(failCb) {
        return this.then(undefined, failCb)
    }
    
    /**
     * 状态无论成功还是失败都将执行回调
     * finally的回调cb不接收链式调用传递的值,他返回的值也不会传递给下一个then函数
     * cb函数的返回值如果为一个MyPromise对象则需要等待MyPromise对象执行完成
     * 支持链式调用
     * @param {function} cb 回调函数
     * */ 
    finally(cb) {
        return this.then((value) => {
            // 返回一个需要等待MyPromise.resolve(cb())执行完毕的MyPromise实例
            // 即使cb返回的是一个MyPromise实例,仍然直接将上一个then的结果传递给下一个then,无视cb返回的MyPromise实例的值
            return MyPromise.resolve(cb()).then(() => value)
        }, (err) => {
            return MyPromise.reject(cb()).then(undefined, () => err)
        })
    }

    /**
     *解决 
     * 将状态改变为成功，储存传入的值。
     * 如果resolve在执行时发现有待执行的成功回调，则逐个调用储存的回调
     * @param {any} value
    */ 
    resolve = (value) => {
        // 如果不是等待状态就不在执行后面的函数
        if (this._state !== WAITONG) return

        this._state = SOLVE
        this._value = value
        
        while(this._solveCb.length) {
            // 一般是状态异步变更时
            this._solveCb.shift()()
        }
    }

    /**
     * 失败
     * 将状态改变为失败，储存传入的值。
     * @param {any} value
    */ 
    reject = (errMsg) => {
        if (this._state !== WAITONG) return

        this._state = FAULT
        this._errMsg = errMsg

        while(this._faultCb.length) {
            this._faultCb.shift()()
        }
    }

    /**
     * 解决异步并发问题
     * 在array中所有的MyPromise为成功后返回对应值的数组
     * 如果array的值不是一个MyPromise实例,这直接将其放入到结果中对应索引的位置
     * 如果array中有一个MyPromise为失败,则all函数为失败
     * @param {array} array
    */
    static all(array) {
        const result = [] // 所有MyPromise成功后的结果
        const len = array.length // 总的长度
        let count = 0 // 记录执行完毕的数量

        return new MyPromise((resolve, reject) => {
            for (let i = 0; i < len; i++) {
                const item = array[i]
                if (item instanceof MyPromise) {
                    // 如果为MyPromise对象则等待执行,有任何一个失败时则直接返回错误
                    item.then((val) => setData(i, val), (val) => reject(val))
                } else {
                    // 如果为一般数据则直接保存
                    setData(i, item)
                }
            }
            
            // 保存结果
            function setData(i, value) {
                result[i] = value
                count++
    
                if (count >= len) resolve(result)
            }
        })
    }

    /**
     * 静态resolve方法,将传入的value值转换成一个状态为成功的MyPromise实例
     * 如果value为MyPromise实例则直接返回
    */
    static resolve(value) {
        if (value instanceof MyPromise) return value

        return new MyPromise(resolve => resolve(value))
    }

    /**
     * 静态reject方法,将传入的value值转换成一个状态为失败的MyPromise实例
     * 如果value为MyPromise实例则直接返回
    */
   static reject(value) {
        if (value instanceof MyPromise) return value

        return new MyPromise((resolve, reject) => reject(value))
    }
}